﻿/*
 * Copyright 2014 Francois Karman
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Linq;
using System.Reflection;

namespace Quartz.WebManager.BuildExt
{
    /// <summary>
    /// Tries to identify the missing attributes on test methods and classes.
    /// </summary>
    [TestClass]
    public class TestAdvisor
    {
        /// <summary>
        /// Retrieves the public classes without the <c>TestClass</c> attribute.
        /// </summary>
        [TestMethod]
        public void PublicClassesWithoutAttribute()
        {
            Assembly assembly = typeof(TestAdvisor).Assembly;
            var types = assembly.GetTypes().Where(t => t.IsPublic && !t.IsAbstract && !t.IsInterface);
            foreach (Type type in types)
            {
                TestClassAttribute attribute = type.GetCustomAttribute<TestClassAttribute>();
                Assert.IsNotNull(attribute, "The type {0} is public and doesn't have a TestClass attribute.", type.Name);
            }
        }

        /// <summary>
        /// Retrieves the non public classes that are tagged with the <c>TestClass</c> attribute.
        /// </summary>
        [TestMethod]
        public void NonPublicClassesWithAttribute()
        {
            Assembly assembly = typeof(TestAdvisor).Assembly;
            var types = assembly.GetTypes().Where(t => t.IsNotPublic && !t.IsAbstract && !t.IsInterface);
            foreach (Type type in types)
            {
                TestClassAttribute attribute = type.GetCustomAttribute<TestClassAttribute>();
                Assert.IsNull(attribute, "The type {0} is not public and has a TestClass attribute.", type.Name);
            }
        }

        /// <summary>
        /// Retrieves the public test methods without the <c>TestMethod</c> attribute.
        /// </summary>
        [TestMethod]
        public void PublicMethodsWithoutAttribute()
        {
            Assembly assembly = typeof(TestAdvisor).Assembly;
            var types = assembly.GetTypes().Where(t => t.IsPublic && !t.IsAbstract && !t.IsInterface);
            foreach (Type type in types)
            {
                // Check whether it is a test class
                TestClassAttribute classAttribute = type.GetCustomAttribute<TestClassAttribute>();
                if (classAttribute != null)
                {
                    var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
                    foreach (MethodInfo method in methods)
                    {
                        TestMethodAttribute attribute1 = method.GetCustomAttribute<TestMethodAttribute>();
                        TestInitializeAttribute attribute2 = method.GetCustomAttribute<TestInitializeAttribute>();
                        if (attribute1 == null && attribute2 == null)
                        {
                            Assert.Fail("The method {0}.{1} is public and doesn't have a test attribute like TestMethod", type.Name, method.Name);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Retrieves the non public methods from test classes that are tagged with the <c>TestMethod</c> attribute.
        /// </summary>
        [TestMethod]
        public void NonPublicMethodsWithAttribute()
        {
            Assembly assembly = typeof(TestAdvisor).Assembly;
            var types = assembly.GetTypes().Where(t => t.IsPublic && !t.IsAbstract && !t.IsInterface);
            foreach (Type type in types)
            {
                // Check whether it is a test class
                TestClassAttribute classAttribute = type.GetCustomAttribute<TestClassAttribute>();
                if (classAttribute != null)
                {
                    var methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
                    foreach (MethodInfo method in methods)
                    {
                        TestMethodAttribute attribute = method.GetCustomAttribute<TestMethodAttribute>();
                        Assert.IsNull(attribute, "The method {0}.{1} is not public and has a TestMethod attribute", type.Name, method.Name);
                    }
                }
            }
        }
    }
}
